﻿using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;

namespace XamlMvp
{
    internal class AutoEventBinder
    {
        readonly Type viewType;        
        Collection<AutoEventBinding> bindings;

        public Type ViewType
        {
            get { return viewType; }
        }

        public AutoEventBinder(Type viewType)
        {
            this.viewType = viewType;
        }

        public void BindEvents(IView view, object presenter)
        {
            if (view.IsAutoEventWireUp)
            {
                if (bindings == null)
                {
                    CreateBindings(presenter.GetType());
                }

                foreach (var item in bindings)
                {
                    item.BindEvent(view, presenter);
                }                
            }
        }

        public void UnBindEvents(IView view, object presenter)
        {
            if (view.IsAutoEventWireUp)
            {
                if (bindings == null)
                {
                    CreateBindings(presenter.GetType());
                }

                foreach (var item in bindings)
                {
                    item.UnBindEvent(view, presenter);
                }
            }
        }

        void CreateBindings(Type presenterType)
        {
            bindings = new Collection<AutoEventBinding>();
            var methods = presenterType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
            var eventFinder = new EventFinder(viewType);
            foreach (var m in methods)
            {
                var auto = m.GetCustomAttributes(typeof(AutoEventAttribute), true).Cast<AutoEventAttribute>();
                var conventionBased = true;
                foreach (var a in auto)
                {
                    var e = eventFinder.Find(a.EventName);
                    if (e != null)
                    {
                        if (a.EventName == m.Name)
                        {
                            conventionBased = false;
                        }

                        bindings.Add(new AutoEventBinding(e, m));
                    }
                }
                if (conventionBased)
                {
                    const int senderArg = 0;
                    const int eventArg = 1;
                    var p = m.GetParameters();
                    if (p.Length == 2 && typeof(object) == p[senderArg].ParameterType && typeof(EventArgs).IsAssignableFrom(p[eventArg].ParameterType))
                    {
                        var e = eventFinder.Find(m.Name);
                        if (e != null)
                        {
                            bindings.Add(new AutoEventBinding(e, m));
                        }
                    }
                }
            }
        }
    }
}
